#ifdef HAS_D3D
# include <d3d9.h>
# include <d3dx9.h>
#endif

/// Include this first, because it seems to mess with Carbon on MacOS X!
#include "../../Epsilon/window.h"
#include "../../Epsilon/timer.h" // Class: eTimer

#include "../../Epsilon/STL.h"
#include "../../Epsilon/Epsilon3D/rendercontext.h"
#include "../../Epsilon/Epsilon3D/texture.h"

#include "structs.h"
#include "util.h"
#include "states.h"
#include "constants.h" // Stuff like NO_BITMAP...
#include "../console.h"
#include "../window.h"
#include "render_batch.h" // Object: batch_processor

#ifndef min
#define min(x1,x2) (((x1) > (x2)) ? (x2) : (x1))
#endif

#ifndef max
#define max(x1,x2) (((x1) > (x2)) ? (x1) : (x2))
#endif




// ------------------------
//  Render Utility Toolkit
// ------------------------

float max_aniso = -1.0f;

void
SetAnisotropy (void)
{
  if (max_aniso == -1.0f) {
    glGetFloatv (GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_aniso);
  }

  glTexParameterf (GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_aniso);
}


void
SetTexture (e3dTexture* tex)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  if (tex != NULL) {
    tex->Apply (Epsilon3D::Texture::TexUnit0);

    SetAnisotropy ();

    if (states->r_tex) {
      tex->Enable ();
    } else {
      tex->Disable ();
    }
  }
}

void
SetNormalMap (e3dTexture* tex)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  if (tex != NULL) {
    glActiveTexture (GL_TEXTURE1_ARB);
    tex->Apply (Epsilon3D::Texture::TexUnit0);

    SetAnisotropy ();

    // Do not set the fixed-function texture enable/disable state,
    //  we NEVER use normal maps in the fixed-function render path.
    /*
    if (states->r_tex) {
      tex->Enable ();
    } else {
      tex->Disable ();
    }
    */
    glActiveTexture (GL_TEXTURE0_ARB);
  }
}

#include "../../Epsilon/Epsilon3D/texture.h"
#include "../../Epsilon/file.h"
#include "../../Epsilon/Epsilon3D/image.h"
bool LoadCoordDebugTexture (e3dTexture** tex);


#include "../font.h"
extern eFont* font;

e3dTexture*
MakeDOT3 (void)
{
  static e3dTexture* tex = NULL;

  if (tex == NULL) {
    TCHAR szTexture [MAX_PATH];
    _stprintf (szTexture, _T ("coords.tga"));

    eStringA texture_name (szTexture);

    eDiskFile file;
    if (file.open (texture_name, eFileMode::Read)) {
      Epsilon::Image::TGA img (file);

      if (img.Load ()) {
        img.GenerateDOT3 ();
        //tex = states->window->rc->CreateTexture2D (&img, false);
        ::SaveTGAFile ("dot3.tga", img.Width (), img.Height (), img.Bpp (), img.Data ());

        _stprintf (szTexture, _T ("dot3.tga"));//kt_rock_1f_shiny_dk.tga"));

        eStringA dot3_name (szTexture);
        file.close ();

        if (file.open (dot3_name, eFileMode::Read)) {
          Epsilon::Image::TGA img (file);

          if (img.Load ()) {
            tex = states->window->rc->CreateTexture2D (&img, false);
            return tex;
          }
        }
      }
    }
  } else {
    return tex;
  }

  return NULL;
}


bool
ApplyTexture (unsigned int idx)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  LoadTexture (idx, false);

  if ((! states->r_tex_align)) {
    SetTexture   (states->ulTextures   [idx]);
    SetNormalMap (states->ulTexNormals [idx]);
    //SetTextureD3D (states->ulTextures [idx]);
  } else {
   // Enhanced Tex Alignment :: Tex-Wireframe
   /* Temp Hack : Associate with cl_all_rooms */
    if (states->cl_all_rooms) {
      glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
      glEnable    (GL_BLEND);
      glDisable   (GL_CULL_FACE);
      glEnable    (GL_ALPHA_TEST);
      glAlphaFunc (GL_NOTEQUAL, 0.0f);
    }
//     SetTexture (::font->GetTex ());
    SetTexture (states->ulTextures [MAX_TEXTURES]);
    //SetTextureD3D (states->ulTextures [MAX_TEXTURES]);
  }

  return true;
}

#include "textures.h" // Underlight::TextureManager 

bool
LoadTexture (const unsigned int index, const bool& bForceLoad)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  if (! bForceLoad && ! states->r_tex)
    return false;

  if (index > MAX_TEXTURES)
    return false;

  if (! states->loadedTextures [index]) {
    // Set the loaded state to true, even if we fail.
    states->loadedTextures [index] = true;

    if (index == MAX_TEXTURES)
      return LoadCoordDebugTexture (&states->ulTextures [index]);

    Underlight::TextureType type = Underlight::Wall;
    BITMAPINFO_4DX*         tex  = Underlight::textures->LoadTexture (index);

    if (! (TextureFrom4DX   (tex, &states->ulTextures   [index], type) &&
           NormalMapFrom4DX (tex, &states->ulTexNormals [index], type))) {
      eTB_Printf ("Texture: %d was unloadable!\n", index);
    } else {
      return true;
    }
  } else {
    return true;
  }

  return false;
}


bool
ApplyFlatTexture (unsigned int idx)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  LoadFlatTexture (idx, false);

  if ((! states->r_tex_align)) {
    SetTexture   (states->ulFlatTextures [idx]);
    SetNormalMap (states->ulFlatNormals  [idx]);
    //SetTextureD3D (states->ulFlatTextures [idx]);
  } else {
    SetTexture (states->ulFlatTextures [MAX_FLATS]);
    //SetTextureD3D (states->ulFlatTextures [MAX_FLATS]);
  }

  return true;
} 

bool
LoadFlatTexture (const unsigned int index, const bool& bForceLoad)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  if (! bForceLoad && ! states->r_tex)
    return false;

  if (index > MAX_FLATS)
    return false;
  
  if (! states->loadedFlatTextures [index]) {
    // Set the loaded state to true, even if we fail.
    states->loadedFlatTextures [index] = true;

    if (index == MAX_FLATS)
      return LoadCoordDebugTexture (&states->ulFlatTextures [index]);

    Underlight::TextureType type = Underlight::Flat;
    BITMAPINFO_4DX*         flat = Underlight::textures->LoadFlat (index);

    if (! (  TextureFrom4DX (flat, &states->ulFlatTextures [index], type) &&
           NormalMapFrom4DX (flat, &states->ulFlatNormals  [index], type))) {
      eTB_Printf ("Flat: %d was unloadable!\n", index);
    } else {
      return true;
    }
  } else {
    return true;
  }

  return false;
}

#include "effect.h" // Underlight::Effects

bool
ApplyEffectTexture (unsigned int idx)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  LoadEffectTexture (idx);

  if ((! states->r_tex_align)) {
    SetTexture (states->ulEffectTextures [idx]);
    //SetTextureD3D (states->ulEffectTextures [idx]);
  } else {
    SetTexture (states->ulEffectTextures [MAX_BITMAPS]);
    //SetTextureD3D (states->ulEffectTextures [MAX_BITMAPS]);
  }

  return true;
}

bool
ApplyAnimatedEffect (unsigned int idx, unsigned int frame)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  LoadEffectTexture (idx);

  if ((! states->r_tex_align)) {
    SetTexture (states->ulEffectTextures [idx + frame]);
    //SetTextureD3D (states->ulEffectTextures [idx + frame]);
  } else {
    SetTexture (states->ulEffectTextures [MAX_BITMAPS]);
    //SetTextureD3D (states->ulEffectTextures [MAX_BITMAPS]);
  }

  return true;
}

bool
LoadEffectTexture (const unsigned int index)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  if (index > MAX_BITMAPS)
    return false;

  if (! states->loadedEffectTextures [index]) {
    // Set the loaded state to true, even if we fail.
    states->loadedEffectTextures [index] = true;

    if (index == MAX_BITMAPS)
      return LoadCoordDebugTexture (&states->ulEffectTextures [index]);

    int idx = Underlight::effects->LoadEffectBitmaps (index);

    if (idx == NO_BITMAP)
      return false;

    Underlight::VisualEffectHeader* header =
        Underlight::effects->GetBitmapHeader (index);

    if ((! header) || header->palette < 0) /*|| header->count < 1) */
      return false;

    for (int i = 0; i < header->frames; i++) {
      Underlight::TextureType type   = Underlight::VisualEffect;
      BITMAPINFO_4DX*         effect = Underlight::effects->GetBitmap (idx + i);

      if (! TextureFrom4DX ( effect,
                              &states->ulEffectTextures [index + i],
                              type )) {
        eTB_Printf ( "Visual Effect: %d (Frame: %d) was unloadable!\n",
                       index,
                        i );
      }

      states->loadedEffectTextures [index + i] = true;
    }
  }

  return true;
}

#include "textures.h"
#include "effect.h"

#define ApplyVisualEffect(id) ( header->frames > 1 ?                \
                                  ApplyAnimatedEffect (id, frame) : \
                                  ApplyEffectTexture  (id) )

void
DrawDebugTile (int tex_id, int tex_type)
{
  int x_origin;
  int y_origin;

  eTB_RenderWindow* window = ::states->window;

  int screen_width  = window->viewport.Width  ();
  int screen_height = window->viewport.Height ();

  int tile_width  = 0;
  int tile_height = 0;

  switch (tex_type)
  {
    /* Texture */
    case 0:
    {
      Underlight::TextureHeader* header =
        Underlight::textures->GetTextureHeader (tex_id);

      static int last_tex = 0;

      //extern bool ApplyEffectTexture (unsigned int idx);
      if (! (header != NULL && ApplyTexture (tex_id))) {
        if (header != NULL) {
          if (! LoadTexture (tex_id, true)) {
            if (last_tex != tex_id)
              eTB_Printf ("Could not apply texture id! (%d)\n", tex_id);
            last_tex = tex_id;
            return;
          } else {
            if (! ApplyTexture (tex_id)) {
              if (last_tex != tex_id)
                eTB_Printf ("Could not apply texture id! (%d)\n", tex_id);
              last_tex = tex_id;
              return;
            }
          }
        } else {
          if (last_tex != tex_id)
            eTB_Printf ("Could not find header for texture id! (%d)\n",tex_id);
          last_tex = tex_id;
          return;
        }
      }

      /* Print extra debug info, ONLY when selecting a NEW debug texture... */
      if (last_tex != tex_id) {
        eTB_Printf (" Texture %d: (%d x %d) - Palette: %d - File Pos: %d\n",
          header->id,
            header->width,
              header->height,
                header->palette,
                  header->file_position);
      }

      last_tex = tex_id;

      tile_width  = header->width;
      tile_height = header->height;

      x_origin = screen_width;
      y_origin = 0;
    } break;

    /* Flat */
    case 1:
    {
      Underlight::FlatHeader* header =
        Underlight::textures->GetFlatHeader (tex_id);

      static int last_flat = 0;

      //extern bool ApplyEffectTexture (unsigned int idx);
      if (! (header != NULL && ApplyFlatTexture (tex_id))) {
        if (header != NULL) {
          if (! LoadFlatTexture (tex_id, true)) {
            if (last_flat != tex_id)
              eTB_Printf ("Could not apply flat texture: (%d)\n", tex_id);
            last_flat = tex_id;
            return;
          } else {
            if (! ApplyFlatTexture (tex_id)) {
              if (last_flat != tex_id)
                eTB_Printf ("Could not apply flat texture: (%d)\n", tex_id);
              last_flat = tex_id;
              return;
            }
          }
        } else {
          if (last_flat != tex_id)
            eTB_Printf ("Could not find header for flat: (%d)\n", tex_id);
          last_flat = tex_id;
          return;
        }
      }

      tile_width  = header->width;
      tile_height = header->height;

      x_origin = tile_width;
      y_origin = 0;

      last_flat = tex_id;
    } break;

    /* Effect */
    case 2:
    {
      /* Include a timer for animated visual effects... */
      static eTimer fx_timer;
      fx_timer.tick ();

      const float time = fx_timer.time ();

      Underlight::VisualEffectHeader* header =
        Underlight::effects->GetBitmapHeader (tex_id);

      int frame = header ? ((int)(time * 10.0f) % header->frames) : 0;

      static int last_effect = 0;

      if (! (header != NULL && ApplyVisualEffect (tex_id))) {
        if (header != NULL) {
          if (! LoadEffectTexture (tex_id)) {
            //if (last_effect != id)
              //eTB_Printf ("Could not apply effect texture: (%d)\n", id);
            last_effect = tex_id;
            return;
          } else {
            if (! ApplyVisualEffect (tex_id)) {
              //if (last_effect != id)
                //eTB_Printf ("Could not apply effect texture: (%d)\n", id);
              last_effect = tex_id;
              return;
            }
          }
        } else {
          //if (last_effect != id)
            //eTB_Printf ("Could not find header for effect texture: (%d)\n", id);
          last_effect = tex_id;
          return;
        }
      }

      /* We selected a new texture id, let's print some debug info... */
      if (last_effect != tex_id) {
        eTB_Printf (" Effect %d:  %d frame(s), %d view(s), palette (%d),"
                    " %d bpp, %d ref(s).  (%d x %d)\n",
          header->id,
            header->frames,
              header->views,
                header->palette,
                  header->bpp * 8,
                    header->count,
                      header->width,
                        header->height );
      }

      last_effect = tex_id;

      tile_width  = -header->width;
      tile_height = -header->height;

      x_origin = 0;//tile_width;
      y_origin = screen_height;// - tile_height;
    } break;
  }

  struct TextureVertex
  {
    int           pos   [4];
    unsigned char color [4];
    float         st    [2];
  };

  TextureVertex aTextureVertices [4];
  TextureVertex* pVert = aTextureVertices;

  float color = 1.0f;

  for (int i = 0; i < 4; i++) {
            aTextureVertices [i].pos [2] = 0;
            aTextureVertices [i].pos [3] = 1; // w = 1.0f
    (DWORD&)aTextureVertices [i].color   = D3DCOLOR_COLORVALUE (1.0f, 1.0f, 1.0f, color);
  }

  pVert->pos [0] = x_origin;
  pVert->pos [1] = y_origin + tile_height;
  pVert->st  [0] = 0.0f;
  pVert->st  [1] = 0.0f;

  pVert++;

  pVert->pos [0] = x_origin;
  pVert->pos [1] = y_origin;
  pVert->st  [0] = 0.0f;
  pVert->st  [1] = 1.0f;

  pVert++;

  pVert->pos [0] = x_origin - tile_width;
  pVert->pos [1] = y_origin + tile_height;
  pVert->st  [0] = 1.0f;
  pVert->st  [1] = 0.0f;

  pVert++;

  pVert->pos [0] = x_origin - tile_width;
  pVert->pos [1] = y_origin;
  pVert->st  [0] = 1.0f;
  pVert->st  [1] = 1.0f;

  glPushAttrib       (GL_ALL_ATTRIB_BITS);
  glPushClientAttrib (GL_CLIENT_ALL_ATTRIB_BITS);

  glMatrixMode   (GL_MODELVIEW);
  glPushMatrix   ();
  glLoadIdentity ();

  glMatrixMode   (GL_PROJECTION);
  glPushMatrix   ();
  glLoadIdentity ();

  glOrtho (0, screen_width, 0, screen_height, -1.0f, 1.0f);

  glDisable   (GL_CULL_FACE);
  glDisable   (GL_DEPTH_TEST);
  glFrontFace (GL_CW);

//  cli_parse::states->render_context->SetCullFaces (e3dRenderStates::False);
//  cli_parse::states->render_context->SetDepthTest (e3dRenderStates::False);

  glDisable (GL_COLOR_MATERIAL);
  glEnable  (GL_TEXTURE_2D);

  glDisableClientState (GL_COLOR_ARRAY);

  glEnable    (GL_ALPHA_TEST);
  glAlphaFunc (GL_NOTEQUAL, 0.0f);

  glEnable    (GL_BLEND);
  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

  glBegin (GL_TRIANGLE_STRIP);

  glColor4f (1.0f, 1.0f, 1.0f, 1.0f);

  glTexCoord2f (aTextureVertices [0].st [0],
                aTextureVertices [0].st [1]);

  glVertex2i (aTextureVertices [0].pos [0],
              aTextureVertices [0].pos [1]);

  glTexCoord2f (aTextureVertices [1].st [0],
                aTextureVertices [1].st [1]);

  glVertex2i (aTextureVertices [1].pos [0],
              aTextureVertices [1].pos [1]);

  glTexCoord2f (aTextureVertices [2].st [0],
                aTextureVertices [2].st [1]);

  glVertex2i (aTextureVertices [2].pos [0],
              aTextureVertices [2].pos [1]);

  glTexCoord2f (aTextureVertices [3].st [0],
                aTextureVertices [3].st [1]);

  glVertex2i (aTextureVertices [3].pos [0],
              aTextureVertices [3].pos [1]);

  glEnd ();

//  glDisable (GL_BLEND);

  //glMatrixMode (GL_PROJECTION); // Redundant
  glPopMatrix  ();

  glMatrixMode (GL_MODELVIEW);
  glPopMatrix  ();

  glPopClientAttrib ();
  glPopAttrib       ();

  //cli_parse::states->render_context->SetCullFaces (e3dRenderStates::True);
  //cli_parse::states->render_context->SetDepthTest (e3dRenderStates::True);
} //DrawCursor

int
SelectNextVisualEffect (int id)
{
  /* Increment the effect id until we reach a valid visual effect ID... */
  while (id < (MAX_BITMAPS - 1)) {
    id++;

    Underlight::VisualEffectHeader* header =
      Underlight::effects->GetBitmapHeader (id);

    /* We found something! - Yay :) */
    if (header != NULL)
      break;
  }

  return id;
}

int
SelectPrevVisualEffect (int id)
{
  /* Decrement the effect id until we reach a valid visual effect ID... */
  while (id > 0) {
    id--;

    Underlight::VisualEffectHeader* header =
      Underlight::effects->GetBitmapHeader (id);

    /* We found something! - Yay :) */
    if (header != NULL)
      break;
  }

  return id > 0 ? id : 0;
}

#include "projectile.h" // For the number of lights in deferred mode...
                        //  there must be a better solution :P

/* Draws a stat overlay for things like FPS, pixel/triangle rate, number of
     batches issued, etc... */
void
DrawStats (void)
{
  eTB_RenderWindow* window = ::states->window;
  

  if (states->r_stat_overlay) {
    window->color_printf (0xff00ff00, -1, -1,  _T ("%4.2f FPS"), window->rc->GetFPS ());

    float fillrate = window->win->getWidth () * window->win->getHeight () * window->rc->GetFPS ();
    window->color_printf (0xff00ffff, -1, window->win->getHeight () - 32, _T ("%1.1f MPix/s"), fillrate / 1000000.0f);

    float tri_rate = (float)cli_parse::batch_processor.GetNumTriangles () * window->rc->GetFPS ();
    window->color_printf (0xff0000ff, -1, window->win->getHeight () - 64, _T ("%1.1f MTri/s"), tri_rate / 1000000.0f);

    //int batch_count = cli_parse::batch_processor.GetNumBatches ();
    //window->color_printf (0xffff0000, -1, g_Window->win->getHeight () - 96, _T ("%d Batches"), batch_count);

    // TODO: State Variable
#ifdef PRINT_COORDS
    window->color_printf (0xffffffff, -1, window->win->getHeight () - 96, _T ("(%2.0fx%2.0f)"),
      cli_parse::states->cl_player.GetX (),
        cli_parse::states->cl_player.GetY ());
#endif

    extern void DrawDeferredStats (void);
    DrawDeferredStats ();
    //window->outline_printf (0xffffffff, 64, 32, _T ("This is a test..."));
  }
//  window->color_printf (0xffff00ff, 0,  -1,  _T ("cli_parse 2.0"));

  if (cli_parse::states->cl_pause && states->r_stat_overlay) {
    const TCHAR* paused = _T ("[Time Paused]");

    // Print "[Time Paused]" in the center of the screen...
    int width  = states->window->viewport.Width  ();
    int height = states->window->viewport.Height ();

    // Determine the "center" position for the text...
    int iPosX, iPosY;

    // TODO: The string width / height only changes when the font changes...
    //         it is wasteful to re-calculate this on every frame.
    iPosX = (width  / 2) - (int)(states->r_font->StringWidth  (paused) / 2.0f);
    iPosY = (height / 2) + (int)(states->r_font->StringHeight ()       / 2.0f);

    window->color_printf (0xff00ff00, iPosX, iPosY, _T ("%s"), paused);
  }
}


///// TODO: Add a module-based post-processing stage, this will be called from there...

// Temp hack, this makes the elegant solution we once had really sloppy!
void
cli_parse::DrawPostProcessing (void)
{
  if (cli_parse::states->r_debug_tex_id) {
    DrawDebugTile (cli_parse::states->r_debug_tex_id, 0);
  }

  if (cli_parse::states->r_debug_flat_id) {
    DrawDebugTile (cli_parse::states->r_debug_flat_id, 1);
  }

  if (cli_parse::states->r_debug_effect_id) {
    DrawDebugTile (cli_parse::states->r_debug_effect_id, 2);
  }

  DrawStats ();
}






// ----------------------------
//  Underlight Utility Toolkit
// ----------------------------

#include "../../Epsilon/Epsilon3D/rendercontext.h"
#include "../../Epsilon/Epsilon3D/pixel_buffer.h"
#include "../../Epsilon/file.h"
#include "../input.h"

#include "level.h"
#include "room.h"
#include "sector.h"
#include "line.h"

extern int GameError (int reason);


namespace Underlight
{
Room* room_from_sector (Sector* sector)
{
  if (! lvl)
    return NULL;

  return lvl->RoomFromSector (sector->id);
}

Room* room_from_pos (float x, float y)
{
  if (! lvl)
    return NULL;

  size_t rooms = lvl->rooms.size ();

  for (unsigned int i = 0; i < rooms; i++) {
    e3dVector2 vec (x, y); /// gcc doesn't like this inline - reference
    if (lvl->rooms [i]->bound.PointInBounds (vec)) {
      return lvl->rooms [i];
      //return lvl->GetRoom (lvl->rooms [i]->id);
    }
  }

  return NULL;
}

Sector* get_sector (Room* room, int id)
{
  return room->GetSector (id);
}

void
DumpSector (Sector* sector)
{
//  assert (sector != NULL);

  Room* room = room_from_sector (sector);

  eTB_Printf (_T ("Sector %d  (Room: %5d\t - First Line: %5d):\n"), sector->id,
                              (room != NULL ? room->id : -1),
                                sector->firstline->num);

  eTB_Printf (_T ("\t>> Walls : %d\n"), sector->walls.size ());

  if (sector->SlopingFloor ())
    eTB_Printf (_T ("\t>  Sloping Floor\n"));
  if (sector->floor && sector->floor->Baseline ())
    eTB_Printf (_T ("\t>> Floor Baseline: %d\n"),
                       sector->floor->Baseline ()->num);

  if (sector->SlopingCeiling ())
    eTB_Printf (_T ("\t>  Sloping Ceiling\n"));
  if (sector->ceil && sector->ceil->Baseline ())
    eTB_Printf (_T ("\t>> Ceiling Baseline: %d\n"),
                      sector->ceil->Baseline ()->num);


   eTB_Printf (_T ("\t * Bitmaps:  [Floor: %d, Ceiling: %d]\n"),
                         sector->floor_bitmap,
                           sector->ceiling_bitmap);

   eTB_Printf (_T ("\t [Flags]: %X\n"), sector->flags);


   eTB_Printf (_T ("\t Tessellation Results: "));

   if (sector->simple)
     eTB_Printf (_T ("<Simple>\n"));
   else
     eTB_Printf (_T ("<Extruded>\n"));


     if (sector->floor && sector->floor->Baseline ()) {
       if (sector->floor->Baseline ()->flags & LINE_SECTOR_FLOOR_BASE)
         eTB_Printf (" Baseline is THIS sector's floor base.\n");
       if (sector->floor->Baseline ()->flags & LINE_FACING_FLOOR_BASE)
         eTB_Printf (" Baseline is FACING sector's floor base.\n");
     }

     eTB_Printf (_T ("\t X Anim: %d, Y Anim: %d\n"),
                      sector->xanim_bottom,
                        sector->yanim_bottom);

//   if (sector->nodraw)
//     eTB_Printf (_T ("\t  <No Draw>\n"));
#if 0
  //if (GetLine (l).sector > NumberOfSectors)
    //PrintError ("Line occupies an invalid sector!");


  eTB_Printf (_T ("\t>> Facing Sector: %d\n"),
                line->facingsector ? line->facingsector->id : -1);

//    if (! line->facingsector)
//      PrintError ("Line faces an invalid sector!");


  // Check the line for texture data
  if (line->fwall_bitmap)
    eTB_Printf (_T ("\t>> Wall Texture: %d\n"),                 line->fwall_bitmap);

  if (line->animation_end != line->fwall_bitmap)
    eTB_Printf (_T ("\t>> Wall End Frame: %d\n"),               line->animation_end);

  if (line->cwall_bitmap)
    eTB_Printf (_T ("\t>> Wall Top Slice: %d\n"),               line->cwall_bitmap);


  // Print the UV texture map data if relevant
  if (line->fwall_u_offset || line->fwall_v_offset) {
    eTB_Printf (_T ("\t>> Bottom Texture U Offset: %4.1f\n"),   line->fwall_u_offset);
    eTB_Printf (_T ("\t>> Bottom Texture V Offset: %4.1f\n"),   line->fwall_v_offset);
  }

  // Print the UV texture map data if relevant
  if (line->cwall_u_offset || line->cwall_v_offset) {
    eTB_Printf (_T ("\t>> Top Texture U Offset: %4.1f\n"),      line->cwall_u_offset);
    eTB_Printf (_T ("\t>> Top Texture V Offset: %4.1f\n"),      line->cwall_v_offset);
  }
    

  // Print the flags if there are any
  if (line->flags) {
    eTB_Printf (_T ("\t>> Flags: 0x%08x - "),                       line->flags);

    DumpLineFlags (line->flags);
  }

  eTB_Printf ("\t>> Number of Walls: (%d)\n", line->num_floor_walls + line->num_ceil_walls);
  eTB_Printf ("\t>>>  Floor:   %d\n", line->num_floor_walls);
  eTB_Printf ("\t>>>  Ceiling: %d\n", line->num_ceil_walls);

  if (line->generated)
    eTB_Printf ("\t>> GENERATED\n");
#endif
}


Line* GetLine (Room* room, int line_num)
{
  std::vector<Sector*>::const_iterator sector     = room->sectors.begin ();
  std::vector<Sector*>::const_iterator sector_end = room->sectors.end   ();

  while (eSTL::IterValid (sector, sector_end)) {
    std::vector<Sector::Wall*>::const_iterator wall      = (*sector)->walls.begin ();
    std::vector<Sector::Wall*>::const_iterator wall_end  = (*sector)->walls.end   ();

    while (eSTL::IterValid (wall, wall_end)) {
      if ((*wall)->parent_line->num == line_num) {
        return (*wall)->parent_line;
      }

      ++wall;
    }

    ++sector;
  }

  return NULL;
}

void
DumpLineFlags (const int& iFlags)
{
  if (iFlags & BOUND)
    eTB_Printf (_T ("[Bounding Wall] "));

  if (iFlags & TRIP_CROSS)
    eTB_Printf (_T ("[Triggered on Crossing] "));

  if (iFlags & TRIP_ACTIVATE)
    eTB_Printf (_T ("[Triggered on Activation] "));

  if (iFlags & LINE_GOES_TO_RECRUITING)
    eTB_Printf (_T ("[Recruiting] "));

  if (iFlags & LINE_ANIMATED)
    eTB_Printf (_T ("[Animated] "));

  if (iFlags & LINE_GOES_TO_HOUSE)
    eTB_Printf (_T ("[Goes to House] "));

  /*
  if (iFlags & LINE_SECTOR_FLOOR_BASE)
  if (iFlags & LINE_SECTOR_CEILING_BASE)
  if (iFlags & LINE_FACING_FLOOR_BASE)
  if (iFlags & LINE_FACING_CEILING_BASE)
  */

  if (iFlags & LINE_SECTOR_FLOOR_BASE)
    eTB_Printf (_T ("{Sector Floor Base} "));
  if (iFlags & LINE_SECTOR_CEILING_BASE)
    eTB_Printf (_T ("{Sector Ceiling Base} "));

  if (iFlags & LINE_FACING_FLOOR_BASE)
    eTB_Printf (_T ("{Facing Sector Floor Base} "));
  if (iFlags & LINE_FACING_CEILING_BASE)
    eTB_Printf (_T ("{Facing Sector Ceiling Base} "));

  if (iFlags & LINE_S_IMPASS)
    eTB_Printf (_T ("[Cannot be passed] "));

  if (iFlags & LINE_STRETCH_FWALL_HORZ)
    eTB_Printf (_T ("<Stretch Floor Wall Horz> "));
  if (iFlags & LINE_STRETCH_FWALL_VERT)
    eTB_Printf (_T ("<Stretch Floor Wall Vert> "));

  if (iFlags & LINE_STRETCH_CWALL_HORZ)
    eTB_Printf (_T ("<Stretch Ceiling Wall Horz> "));
  if (iFlags & LINE_STRETCH_CWALL_VERT)
    eTB_Printf (_T ("<Stretch Ceiling Wall Vert> "));

  if (iFlags & LINE_NO_WARD)
    eTB_Printf (_T ("[Cannot be Warded] "));

  if (iFlags & LINE_FLIP_FWALL_HORZ)
    eTB_Printf (_T ("<Flip Floor Wall Horz> "));
  if (iFlags & LINE_FLIP_FWALL_VERT)
    eTB_Printf (_T ("<Flip Floor Wall Vert> "));

  if (iFlags & LINE_FLIP_CWALL_HORZ)
    eTB_Printf (_T ("<Flip Ceiling Wall Horz> "));
  if (iFlags & LINE_FLIP_CWALL_VERT)
    eTB_Printf (_T ("<Flip Ceiling Wall Vert> "));

  if (iFlags & LINE_LIGHT)
    eTB_Printf (_T ("(Light) "));


  // Ownership flags

  if (iFlags & LINE_SABLE_MOON)
    eTB_Printf (_T ("[Order of the Sable Moon] "));

  if (iFlags & LINE_ECLIPSE)
    eTB_Printf (_T ("[Alliance of the Eclipse] "));

  if (iFlags & LINE_SHADOW)
    eTB_Printf (_T ("[Keepers of Eternal Shadow] "));

  if (iFlags & LINE_COVENT)
    eTB_Printf (_T ("[Union of the Covenant] "));

  if (iFlags & LINE_RADIANCE)
    eTB_Printf (_T ("[Protectors of the Radiance] "));

  if (iFlags & LINE_CALENTURE)
    eTB_Printf (_T ("[House Calenture] "));

  if (iFlags & LINE_ENTRANCED)
    eTB_Printf (_T ("[Gathering of the Entranced] "));

  if (iFlags & LINE_LIGHT)
    eTB_Printf (_T ("[Dreamers of Light] "));

  eTB_Printf (_T ("\n"));
}


/* Additional properties unique to Trip Lines... */
void
DumpTripLine (TripLine* trip)
{
  // Do nothing if this is not technically a trip line...
  if (! (trip->flags & TRIP_CROSS))
    return;

  eTB_Printf ("\t>> Trip Action(s) :: [0x%08x] :: ", trip->trip_flags);

  if (trip->trip_flags & TRIP_TELEPORT)
    eTB_Printf ("<Teleport> ");
  if (trip->trip_flags & TRIP_GOALPOSTING)
    eTB_Printf ("<Post Goal> ");
  if (trip->trip_flags & TRIP_LEVELCHANGE)
    eTB_Printf ("<Change Level> ");
  if (trip->trip_flags & TRIP_SOUNDEFFECT)
    eTB_Printf ("<Play Sound> ");
  if (trip->trip_flags & TRIP_SHOWMESSAGE)
    eTB_Printf ("<Print Message> ");
  if (trip->trip_flags & TRIP_QUESTBUILDER)
    eTB_Printf ("<Quest Builder> ");

  eTB_Printf ("\n");
}

void
DumpLine (Line* line)
{
  eTB_Printf (_T ("Line %d  (From: %5d\t-  To: %6d):\n"),  line->num,  line->from_idx,
                                                                       line->to_idx);

  /*
  if (NumberOfVertices && GetLine (l).from > NumberOfVertices)
    PrintError ("Line begins at an invalid vertex!");

  if (NumberOfVertices && GetLine (l).to  > NumberOfVertices)
    PrintError ("Line ends at an invalid vertex!");
  */

  Underlight::Room* parent_room =
    room_from_sector (line->sector);

  if (parent_room != NULL)
    eTB_Printf (_T ("\t>> Parent Room: %d\n"),             parent_room->id);


  eTB_Printf (_T ("\t>> Parent Sector: %d\n"),             line->sector->id);

  //if (GetLine (l).sector > NumberOfSectors)
    //PrintError ("Line occupies an invalid sector!");


  eTB_Printf (_T ("\t>> Facing Sector: %d\n"),
                line->facingsector ? line->facingsector->id : -1);

//    if (! line->facingsector)
//      PrintError ("Line faces an invalid sector!");


  // Check the line for texture data
  if (line->fwall_bitmap)
    eTB_Printf (_T ("\t>> Wall Texture: %d\n"),            line->fwall_bitmap);

  if (line->animation_end != line->fwall_bitmap)
    eTB_Printf (_T ("\t>> Wall End Frame: %d\n"),          line->animation_end);

  if (line->cwall_bitmap)
    eTB_Printf (_T ("\t>> Wall Top Slice: %d\n"),          line->cwall_bitmap);


  // Print the UV texture map data if relevant
  if (line->fwall_u_offset || line->fwall_v_offset) {
    eTB_Printf (_T ("\t>> Bottom Texture U Offset: %4.1f\n"),   line->fwall_u_offset);
    eTB_Printf (_T ("\t>> Bottom Texture V Offset: %4.1f\n"),   line->fwall_v_offset);
  }

  // Print the UV texture map data if relevant
  if (line->cwall_u_offset || line->cwall_v_offset) {
    eTB_Printf (_T ("\t>> Top Texture U Offset: %4.1f\n"),      line->cwall_u_offset);
    eTB_Printf (_T ("\t>> Top Texture V Offset: %4.1f\n"),      line->cwall_v_offset);
  }


  // Print the flags if there are any
  if (line->flags) {
    eTB_Printf (_T ("\t>> Flags: 0x%08x - "),                       line->flags);

    DumpLineFlags (line->flags);

    if (line->flags & TRIP_ACTIVATE ||
        line->flags & TRIP_CROSS) {
      DumpTripLine ((Underlight::TripLine*) line);
    }
  }

  eTB_Printf ("\t>> Wall Angle  [Sin: %4.1f Cos: %4.1f]\n", line->SINE, line->COS);

  eTB_Printf ("\t>> Number of Walls: (%d)\n", line->num_floor_walls + line->num_ceil_walls);
  eTB_Printf ("\t>>>  Floor:   %d\n", line->num_floor_walls);
  eTB_Printf ("\t>>>  Ceiling: %d\n", line->num_ceil_walls);

  if (line->generated)
    eTB_Printf ("\t>> GENERATED\n");
}

void
LineHeightOrigins (Line* line, float& floor, float& ceil)
{
  Sector* from_sec = line->sector;
  Sector* to_sec   = line->facingsector;

  if (line->flags & LINE_STRETCH_FWALL_VERT)
  {
    float tosec_floor_height;
    if (line->flags & BOUND)
      tosec_floor_height = from_sec->ceiling_height;
    else if (to_sec->SlopingFloor ())
      tosec_floor_height = max (to_sec->FloorHt (line->x1 (), line->y1 ()),
                                to_sec->FloorHt (line->x2 (), line->y2 ()));
    else
      tosec_floor_height = to_sec->ceiling_height;
  }
}

PickingData
Pick (eInput* input, e3dRenderContext* rc)
{
  int x = (int)input->GetCursorX ();
  int y = (int)input->GetCursorY ();

  if (states->cl_verbose)
    eTB_Printf ("Picking: (%d, %d)...\n", x, y);

#ifdef HAS_D3D
  // Adjust the coordinates in OpenGL
  if (! e3dDirect3D::IsDevicePresent ()) {
#endif
    y = ::states->window->viewport.Height () - y;
#ifdef HAS_D3D
  }
#endif

  cli_parse::states->cl_need_pick = false;

#ifndef DEBUG_GET_PIXELS
  e3dPixelBuffer* pbuffer = rc->GetPixels (x, y, 1);

  unsigned char* data = (unsigned char *)pbuffer->data;
  
#ifdef HAS_D3D
  if (e3dDirect3D::IsDevicePresent ()) {
    unsigned char colorSwap = data [2];
    data [2] = data [0];
    data [0] = colorSwap;
  } else {
#endif
    unsigned char colorSwap = data [2];
    data [2] = data [1];
    data [1] = colorSwap;
    glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#ifdef HAS_D3D
  }
#endif

  int pixel = ((int *)data) [0];
#else
  e3dPixelBuffer* pbuffer = rc->GetPixels ();

  unsigned char* data = (unsigned char *)pbuffer->data;

  //
  // Change image data from BGR to RGB
  //
  //  * GetPixels (...) ought to do this, because Direct3D is ass-backwards
  //
  for (int i = 0; i < (pbuffer->w * pbuffer->h * pbuffer->bpp); i += pbuffer->bpp)
  {
    unsigned char colorSwap = data [i];
    data [i]     = data [i + 2];
    data [i + 2] = colorSwap;
  }

  int pixel = ((int *)data) [(y * pbuffer->w) + x];

  bool
  SaveTGAFile ( const TCHAR*         filename,
                const short int&     width,
                const short int&     height,
                const unsigned char& bits,
                unsigned char* imageData );

  // Uncomment this to save a copy of the framebuffer to "test.tga"
  SaveTGAFile (_T ("test.tga"), pbuffer->w, pbuffer->h, pbuffer->bpp * 8, (unsigned char *)data);
#endif

  delete pbuffer;

  return PickingData (pixel);
}

float
UnderlightAngle (short angle)
{
  const int  Angle_0   =   0;
  const int  Angle_1   =   2;
  const int  Angle_6   =  16;
  const int  Angle_30  =  64;
  const int  Angle_45  = 128;
  const int  Angle_90  = 256;
  const int  Angle_180 = 512;
  const int  Angle_270 = 768;
  const int  Angle_360 =1024;

  switch (angle)
  {
    case Angle_0:
      return 360.0f;
    case Angle_1:
      return 1.0f;
    case Angle_6:
      return 6.0f;
    case Angle_30:
      return 30.0f;
    case Angle_45:
      return 45.0f;
    case Angle_90:
      return 90.0f;
    case Angle_180:
      return 180.0f;
    case Angle_270:
      return 270.0f;
    case Angle_360:
      return 360.0f;
    default:
      /* Interpolate, when angle is not exact... */
////      eTB_Printf ("Unknown Underlight Angle: %d\n", angle);
      return 360.0f * ((float)angle / (float)1024);
  }
}

}




/// --------------------------
///  Image Processing Toolkit
/// --------------------------

void
swap_columns   (unsigned char* data, int width, int height, int bpp)
{
  unsigned char tmp;

  for (int i = 0; i < (width / 2); i++) {
    for (int j = 0; j < height; j++) {
      tmp = data [(width - i - 1) + (width * j)];

      data [(width - i - 1) + (width * j)] = data [i + (width * j)];
      data [i + (width * j)]               = tmp;
    }
  }
}

void
swap_columns_ex   (unsigned char* data, int width, int height, int bpp, int x_off = 0, int y_off = 0)
{
  unsigned char* tmp = new unsigned char [height * width * bpp];
  memcpy (tmp, data, width * height * bpp);

  for (int i = 0; i < y_off; i++) {
    for (int j = 0; j < height; j++) {
      data [(x_off + i) + (width * j)] = tmp [((x_off + y_off) - i) + (width * j)];
    }
  }

  delete [] tmp;
}

void
swap_scanlines (unsigned char* data, int width, int height, int bpp)
{
  int            bytes         = bpp;
  int            scanline_size = width * bytes;

  unsigned char* tmp           = new unsigned char [scanline_size];

  for (int i = 0; i < (height / 2); i++) {
    memcpy (tmp,
                  data + i * scanline_size,
                        scanline_size);

    memcpy (data + i * scanline_size,
                  data + (height - i - 1) * scanline_size,
                        scanline_size);

    memcpy (data + (height - i - 1) * scanline_size,
                  tmp,
                        scanline_size);
  }

  delete [] tmp;
}

bool
SaveTGAFile ( const TCHAR*         filename,
              const short int&     width,
              const short int&     height,
              const unsigned char& bits,
              unsigned char* imageData )
{
  if (! filename)
    return false;

  unsigned char Unused000;  // Garbage collector (1-byte)
  short int     Unused001;  // Garbage collector (2-bytes)
  unsigned char imageType;  // Type of image we're writing
  int           colorMode;  // Color mode
  unsigned char colorSwap;  // Swap buffer
  int           imageIndex; // Index ID
  unsigned char bitDepth;   // Bit depth
  long          imageSize;  // Image data size
  FILE*         fTGA;       // Image file

  // Create our image
  if (! (fTGA = _tfopen (filename, _T ("wbS"))))
  {
    return false;
  }

  switch (bits) {
    case 32:
      imageType = 3;  /* RGBA (BGRA) - Uncompressed */
      bitDepth  = 32;
      colorMode = 4;  /* 32-Bit RGBA */
      break;
    default:
    case 24:
      imageType = 2;  /* RGB (BGR) - Uncompressed */
      bitDepth  = 24;
      colorMode = 3;  /* 24-Bit RGB */
      break;
    case 16:
      imageType = 2;  /* RGB (BGR) - Uncompressed */
      bitDepth  = 16;
      colorMode = 2;  /* 16-Bit RGB */
      break;
  }

//  swap_scanlines (imageData, width, height);

  Unused000 = NULL; // Garbage!
  Unused001 = NULL; // Garbage!

  // Write garbage (No Extra Header, No Compression)
  fwrite (&Unused000, sizeof (unsigned char),  1, fTGA);
  fwrite (&Unused000, sizeof (unsigned char),  1, fTGA);

  // Write imageType
  fwrite (&imageType, sizeof (unsigned char),  1, fTGA);

  // Write more garbage (No Compression)
  fwrite (&Unused001, sizeof (short int),      1, fTGA);
  fwrite (&Unused001, sizeof (short int),      1, fTGA);
  fwrite (&Unused000, sizeof (unsigned char),  1, fTGA);
  fwrite (&Unused001, sizeof (short int),      1, fTGA);
  fwrite (&Unused001, sizeof (short int),      1, fTGA);

  // Write image dimensions
  fwrite (&width,    sizeof (short int),       1, fTGA);
  fwrite (&height,   sizeof (short int),       1, fTGA);
  fwrite (&bitDepth, sizeof (unsigned char),   1, fTGA);

  // Write more garbage (No Flags)
  fwrite (&Unused000, sizeof (unsigned char),  1, fTGA);

  // Calculate image size
  imageSize = width * height * colorMode;

  // Change image data from RGB to BGR
  for (imageIndex = 0; imageIndex < imageSize; imageIndex += colorMode)
  {
    colorSwap = imageData [imageIndex];
    imageData [imageIndex]     = imageData [imageIndex + 2];
    imageData [imageIndex + 2] = colorSwap;
  }

  // Write the image data
  fwrite (imageData, sizeof (unsigned char), imageSize, fTGA);

  // Close the file
  fclose (fTGA);

  return true;
}

/// XXX: TODO Port Me
bool
LoadCoordDebugTexture (e3dTexture** tex)
{
#if 0
  int index = 2044;
  int idx = Underlight::effects->LoadEffectBitmaps (index);
  
  Underlight::VisualEffectHeader* header =
      Underlight::effects->GetBitmapHeader (index);

    if ((! header) || header->palette < 0 || header->count < 1)
      return false;

    for (int i = 0; i < 1; i++) {
      Underlight::TextureType type   = Underlight::VisualEffect;
      BITMAPINFO_4DX*         effect = Underlight::effects->GetBitmap (idx + i);

      if (! TextureFrom4DX ( effect,
                              tex,
                              type )) {
        eTB_Printf ("Visual Effect: %d (Frame: %d) was unloadable!\n", index, i);
      }

//      states->loadedEffectTextures [index + i] = true;
    }

/*
  TCHAR szWorkingDir [MAX_PATH] = {0};
  GetCurrentDirectory (MAX_PATH, szWorkingDir);

  TCHAR szTexture [MAX_PATH];
  _stprintf (szTexture, _T ("%s\\coords.tga"), szWorkingDir);
*/
#endif

  if (! *tex) {
    TCHAR szTexture [MAX_PATH];
    _stprintf (szTexture, _T ("coords.tga"));

    eStringA texture_name (szTexture);

    eDiskFile file;
    if (file.open (texture_name, eFileMode::Read)) {
      Epsilon::Image::TGA img (file);

      if (img.Load ()) {
        *tex = states->window->rc->CreateTexture2D (&img, false);
        return true;
      }
    }
  } else {
    eTB_Printf ("Already loaded coord debug texture...\n");
  }

  return false;
}





// -------------------------
//  Sound Utility "Toolkit"
// -------------------------

#include "../sound.h" // OpenALBuffer

OpenALBuffer*
MakeSoundAL (Underlight::id_t sound_id)
{
  cli_parse::ModuleStates* states = cli_parse::states;

  OpenALBuffer* buffer = states->sound_context->createBuffer ();

  int    size     = Underlight::effects->GetSoundHeader (sound_id)->data_length;
  Underlight::Sound* sound 
                  = Underlight::effects->GetSound       (sound_id);

  ALenum type;

  if (sound->bits_per_sample == 8) {
    if (sound->num_channels == 2)
      type = AL_FORMAT_STEREO8;
    else
      type = AL_FORMAT_MONO8;
  } else {
    if (sound->num_channels == 2)
      type = AL_FORMAT_STEREO16;
    else
      type = AL_FORMAT_MONO16;
  }

  buffer->loadData (sound->data, size, sound->sample_rate, type);

  return buffer;
}
